#include "LecteurPhraseAvecArbre.h"
#include "Exception.h"
#include <stdlib.h>
#include <iostream>
using namespace std;

////////////////////////////////////////////////////////////////////////////////
LecteurPhraseAvecArbre::LecteurPhraseAvecArbre(string nomFich) :
	ls(nomFich), ts(), nbErreurSyntaxe(0) {
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::analyse() {
	arbre=programme();
        if (nbErreurSyntaxe==0){
	cout << "Syntaxe correcte." << endl;
        }
        else{
            cout << "Syntaxe incorrecte : "<<nbErreurSyntaxe<<" erreur(s)" << endl;
            throw (EX_ERREUR_SYNTAXE());
        }
        }

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::programme() {
	// <programme> ::= debut <seq_inst> fin FIN_FICHIER

	sauterSymCour("debut");
	Noeud* si = seqInst();
	sauterSymCour("fin");
	testerSymCour("<FINDEFICHIER>");
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::seqInst() {
	// <seqInst> ::= <inst> ; { <inst> ; }

	NoeudSeqInst* si = new NoeudSeqInst();
	do {
		si->ajouteInstruction(inst());
		sauterSymCour(";");
	} while (ls.getSymCour()=="<VARIABLE>"||
                ls.getSymCour() == "si"||
                ls.getSymCour() == "tantque"||
                ls.getSymCour() == "repeter" ||
                ls.getSymCour() == "lire"||
                ls.getSymCour() == "ecrire"||
                ls.getSymCour() == "pour");
	// tant que le symbole courant est un debut possible d'instruction...
	return si;
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::inst() {
//  <inst> ::= <affectation> | <instSi> | <instTq> | <instRepeter> | <instPour>
    try{
    if(ls.getSymCour() == "<VARIABLE>")
	return affectation();
    else if (ls.getSymCour() == "si")
       return  instSi();
    else if (ls.getSymCour() == "tantque")
       return  instTq();
    else if (ls.getSymCour() == "repeter")
        return instRepeter();
    else if (ls.getSymCour() == "lire")
        return instLire();
    else if (ls.getSymCour() == "ecrire")
        return instEcrire();
    else if (ls.getSymCour() == "pour")
        return instPour();
    else
        erreur("<inst>");
    Noeud* noeud; // pour eviter les attentions
    return noeud;}
    
    catch (EX_ERREUR_SYNTAXE){
        nbErreurSyntaxe++;
        while (ls.getSymCour() != ";"&&ls.getSymCour() != "<FINDEFICHIER>")
        {
            ls.suivant();
        }
        
    }
}


////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::affectation() {
// <affectation> ::= <variable> = <expression> 

	testerSymCour("<VARIABLE>");
	Noeud* var = ts.chercheAjoute(ls.getSymCour());
	ls.suivant();
	sauterSymCour("=");
        Noeud* exp = expression();
        return new NoeudAffectation(var,exp);
    
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::expression() {
// <expression> ::= <facteur> { <opAdd> <facteur> }

	Noeud* fact = terme();
     
	while (ls.getSymCour()=="+" || ls.getSymCour()=="-" ) {
		Symbole operateur = opAdd(); // on stocke le symbole de l'opérateur
		Noeud* factDroit=terme(); // lecture de l'operande droit
		fact = new NoeudOperateurBinaire(operateur,fact,factDroit); // const. du noeud
	}
	return fact;
    
}

////////////////////////////////////////////////////////////////////////////////
Noeud* LecteurPhraseAvecArbre::facteur() {
// <facteur> ::= <entier>  |  <variable>  |  - <facteur>  |  ( <expression> ) | <CHAINE>

	Noeud* fact=NULL;

	if (ls.getSymCour()=="<VARIABLE>" 
            || ls.getSymCour()=="<ENTIER>"
            ||ls.getSymCour()=="<CHAINE>") {
		fact=ts.chercheAjoute(ls.getSymCour());
		ls.suivant();
	} else if (ls.getSymCour()=="-"|| ls.getSymCour()=="non") {
            Symbole operateur = opUnaire();
		// on représente le moins unaire (- facteur) par une soustractin binaire (0 - facteur)
		fact = new NoeudOperateurUnitaire(operateur, expBool());
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expression();
		sauterSymCour(")");
	} else if (ls.getSymCour()=="(") {
		ls.suivant();
		fact=expBool();
		sauterSymCour(")");
	}else
		erreur("<facteur>");
	return fact;
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::testerSymCour(string ch) {
	if (ls.getSymCour() != ch) {
		cout << endl << "-------- Erreur ligne " << ls.getLigne()
				<< " - Colonne " << ls.getColonne() << endl << "   Attendu : "
				<< ch << endl << "   Trouve  : " << ls.getSymCour() << endl
				<< endl;
	throw (EX_ERREUR_SYNTAXE()); // plus tard, on levera une exception
	}
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::sauterSymCour(string ch) {
	testerSymCour(ch);
	ls.suivant();
}

////////////////////////////////////////////////////////////////////////////////
void LecteurPhraseAvecArbre::erreur(string mess) {
	cout << endl << "-------- Erreur ligne " << ls.getLigne() << " - Colonne "
			<< ls.getColonne() << endl << "   Attendu : " << mess << endl
			<< "   Trouve  : " << ls.getSymCour() << endl << endl;
	throw (EX_ERREUR_SYNTAXE()); // plus tard, on levera une exception
}

Noeud* LecteurPhraseAvecArbre::terme(){
   // <terme>::=<facteur>{<oppMult><facteur>}
    Noeud*  terme= facteur();
    while(ls.getSymCour() == "*" || ls.getSymCour() =="/"){
        Symbole operateur = oppMult();
        Noeud * operandeDroit = facteur();
        terme = new NoeudOperateurBinaire(operateur,terme,operandeDroit);
    }
    return terme;
}

Symbole LecteurPhraseAvecArbre::opAdd(){
    //<opAdd>:: = + | -
    Symbole operateur;
    if(ls.getSymCour() == "+" || ls.getSymCour() == "-"){
        operateur = ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<oppAdd>");
    return operateur;
}

Symbole LecteurPhraseAvecArbre::oppMult(){
    //<opMult>::=* | /
    Symbole operateur;
    if(ls.getSymCour() == "*" || ls.getSymCour() == "/"){
        operateur = ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<oppMult>");
    return operateur;
}

Noeud* LecteurPhraseAvecArbre::expBool(){
    //<expBool>::=<expBoolEt> {ou <expBoolEt>}
    Noeud* expBool = expBoolEt();
    while(ls.getSymCour()=="ou") {
        Symbole symbole = opOu();
        expBool = new NoeudOperateurBinaire(Symbole("ou"),expBool,expBoolEt());
    }
    return expBool;
}

Noeud* LecteurPhraseAvecArbre::expBoolEt(){
    //<expBoolEt>::=<relation> {et <relation>}
    Noeud* expBoolEt = relation();
    while(ls.getSymCour()=="et"){
        Symbole symbole = opEt();
        expBoolEt = new NoeudOperateurBinaire(symbole,expBoolEt,relation());
    }
    return expBoolEt;
}

Noeud* LecteurPhraseAvecArbre::relation(){
    //<relation>::=<expresion>{<opRel><expression>}
    Noeud* relation = expression();
    while(ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||ls.getSymCour()=="<" ||ls.getSymCour()=="<=" ||ls.getSymCour()==">" || ls.getSymCour()==">="){
        Symbole operateur = opRel();
        relation = new NoeudOperateurBinaire(operateur,relation,expression());
    }
    return relation;
}
Symbole LecteurPhraseAvecArbre::opRel(){
    //<opRel>::= == | != | <= | < | > | >=
    Symbole operateur;
    if(ls.getSymCour()=="==" || ls.getSymCour()=="!=" ||ls.getSymCour()=="<" ||
            ls.getSymCour()=="<=" ||ls.getSymCour()==">" || ls.getSymCour()==">="){
        operateur = ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<opRel>");
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opUnaire(){
    //<opUnaire>::= - | non
    Symbole operateur;
    if(ls.getSymCour()=="-" || ls.getSymCour()=="non"){
        operateur=ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<opUnitaire>");
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opEt(){
    //<opEt>::= et
    Symbole operateur;
    if(ls.getSymCour()=="et" ){
        operateur=ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<opEt>");
    return operateur;
}

Symbole LecteurPhraseAvecArbre::opOu(){
    //<opOu>::= ou
    Symbole operateur;
    if(ls.getSymCour()=="ou" ){
        operateur=ls.getSymCour();
        ls.suivant();
    }
    else
        erreur("<opOu>");
    return operateur;
}



Noeud* LecteurPhraseAvecArbre::instSi(){
    //<instSi> ::= si ( <expBool> )alors <seqInst> { sinonsi ( <expBool> )alors <seqInst> }[ sinon <seqInst> ] finsi
    sauterSymCour("si");
    sauterSymCour("(");
    Noeud* condicionPrincipal = expBool();
    sauterSymCour(")");
    sauterSymCour("alors");
    NoeudSi* instSi = new NoeudSi(condicionPrincipal, seqInst());
    while(ls.getSymCour()=="sinonsi"){
        ls.suivant();
        sauterSymCour("(");
        instSi->ajouteSinonsiCondition(expBool());
        sauterSymCour(")");
        sauterSymCour("alors");
        instSi->ajouteSinonsiSeqInst(seqInst());
    }
    if(ls.getSymCour()=="sinon"){
        ls.suivant();
        instSi->ajouteSinon(seqInst());
    }
    sauterSymCour("finsi");
    return instSi;
}

Noeud* LecteurPhraseAvecArbre::instTq(){
    //<instTq> ::= tantque ( <expBool> ) <seqInst> fintantque
    NoeudStructureControl* instTq = new NoeudStructureControl("tantque");
    sauterSymCour("tantque");
    sauterSymCour("(");
    instTq->ajouteCondition(expBool());
    sauterSymCour(")");
    instTq->ajouteSeqInt(seqInst());
    sauterSymCour("fintantque");
    return instTq;
}

Noeud* LecteurPhraseAvecArbre::instRepeter(){
    //<instRepeter> ::= repeter <seqInst> jusqua ( <expBool> )
    NoeudFaireJusqua* instFaireJusqua = new NoeudFaireJusqua();
    sauterSymCour("repeter");
    instFaireJusqua->ajouteSeqInt(seqInst());
    sauterSymCour("jusqua");
    sauterSymCour("(");
    instFaireJusqua->ajouteCondition(expBool());
    sauterSymCour(")");
    return instFaireJusqua;
}

Noeud* LecteurPhraseAvecArbre::instLire(){
    SymboleValue* var;
    sauterSymCour("lire");
    sauterSymCour("(");
    if(ls.getSymCour()=="<VARIABLE>"){
        var = ts.chercheAjoute(ls.getSymCour());
        ls.suivant();
    }
    else
        erreur("<lire>");
    sauterSymCour(")");
    return new NoeudLire(var);
}

Noeud* LecteurPhraseAvecArbre::instEcrire(){
    NoeudEcrire* ecrire;
    sauterSymCour("ecrire");
    sauterSymCour("(");
    if(ls.getSymCour()=="<CHAINE>"){
        ecrire = new NoeudEcrire(ts.chercheAjoute(ls.getSymCour()));
        ls.suivant();
    }
    else
        ecrire = new NoeudEcrire(expression());
    sauterSymCour(")");
    return ecrire;
}

Noeud* LecteurPhraseAvecArbre::instPour(){
//<instPour>::=pour([<affectation>{,<affectation>}];<expBool>;[<affectation>{,<affectation>}])<seqInst> finpour
    NoeudPour* pour = new NoeudPour();
    sauterSymCour("pour");
    sauterSymCour("(");
    if(ls.getSymCour()=="<VARIABLE>"){
        pour->ajouteInitialization(affectation());
        while(ls.getSymCour()==","){
            ls.suivant();
            pour->ajouteInitialization(affectation());
        }
    }
    sauterSymCour(";");
    if(ls.getSymCour()== "<VARIABLE>" ||
            ls.getSymCour()== "<ENTIER>" ||
            ls.getSymCour()== "-" ||
            ls.getSymCour()== "(")
        pour->ajouteCondition(expBool());
    else
        erreur("<expBool>");
    sauterSymCour(";");
    if(ls.getSymCour()=="<VARIABLE>"){
        pour->ajouteInitialization(affectation());
        while(ls.getSymCour()==","){
            ls.suivant();
            pour->ajouteInitialization(affectation());
        }
    }
    sauterSymCour(")");
    pour->ajouteSeqInt(seqInst());
    sauterSymCour("finpour");
    return pour;
}

